#include <iostream>
#include <unistd.h>
#include <vector>
#include <cstdio>
#include "Thread.hpp"

// 应用方的视角
std::string GetThreadName()
{
    static int number = 1;
    char name[64];
    snprintf(name, sizeof(name), "Thread-%d", number++);
    return name;
}

void Print(int num)//传入ThreadRoutine的回调函数
{
    while (num)
    {
        std::cout << "hello world: " << num-- << std::endl;
        sleep(1);
    }
}


// int main()
// {
//     // Thread t(Print, GetThreadName());
//     // std::cout << "is thread running? " << t.IsRunning() << std::endl;
//     // t.Start();
//     // std::cout << "is thread running? " << t.IsRunning() << std::endl;
//     // t.Join();



//     // 先描述，再组织：先在class Thread中进行封装，再用vector管理
//     // const int num = 5;//创建5个线程
//     // std::vector<Thread> threads;
//     // for (int i = 0; i < num; i++)
//     // {
//     //     threads.push_back(Thread(Print, GetThreadName()));
//     // }
//     // for (auto &t : threads)
//     // {
//     //     std::cout << t.ThreadName() << ", is running: " << t.IsRunning() << std::endl;
//     // }
//     // for (auto &t : threads)
//     // {
//     //     t.Start();
//     // }
//     // for (auto &t : threads)
//     // {
//     //     std::cout << t.ThreadName() << ", is running: " << t.IsRunning() << std::endl;
//     // }
//     // for (auto &t : threads)
//     // {
//     //     t.Join();
//     // }



//     //加入模板，可以给线程传入任意类型参数
//     Thread<int> t(GetThreadName(), Print, 10);
//     t.Start();
//     t.Join();

//     return 0;
// }


//全局锁
// int ticket = 10000; // 全局的共享资源 -- 临界资源（任何时刻只允许一个线程访问的资源）
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 锁被定义并初始化了，这个锁也是全局的，也属于共享资源
// //全局的锁就是这样定义的，右边把这个宏赋值给锁，这样每个线程都看得到这个锁，也能使用，因为是全局变量

// // 加锁：
// // 1. 要尽可能的给少的代码块加锁（否则会导致效率过低，可以对比加锁前后抢票的速度，明显加锁之后抢票速度慢了）
// // 2. 一般加锁，都是给临界区加锁
// void GetTicket(std::string name)//如果是全局的锁，这里的函数就是这样的参数，无需接受锁
// {
//     while (true)
//     {
//         // 2. 程序员自己要保证所有线程都得加锁，不可以有的加有的不加（否则并发问题仍然存在），规则是每个线程都必须先申请锁
//         // 3. 根据互斥的定义，任何时刻，只允许一个线程申请锁成功！多个线程申请锁失败，失败的线程会在mutex上进行阻塞，本质就是等待！
//         //下面语句对定义的锁进行加锁
//         pthread_mutex_lock(&mutex); // 1. 申请锁本身是安全的，原子的
//         if (ticket > 0) // 4. 一个线程在临界区中访问临界资源的时候，完全可能在还没解锁的时候发生切换，但是注意就算被切换了
//         //这个锁还是锁着的，还没解锁，在这段被切换走的时间里，其他线程也无法访问临界区，这就解决了并发带来的问题，但是也影响了效率
//         {//if语句块就是临界区
//             // 充当抢票花费的时间
//             usleep(1000);
//             printf("%s get a ticket: %d\n", name.c_str(),ticket);
//             ticket--;
//             pthread_mutex_unlock(&mutex);
//         }
//         else
//         {
//             pthread_mutex_unlock(&mutex);//万一加锁了之后只执行了else语句，就相当于没有解锁，所以要在这里解锁
//             break;
//         }
//         //pthread_mutex_unlock(mutex);//不能在这里才解锁，万一执行了else就退出了就没人解锁了
//         // 实际情况，还有后续的动作， TODO?
//     }
// }
// int main()
// {
//     //创建四个线程抢票
//     std::string name1 = GetThreadName();
//     Thread<std::string> t1(name1, GetTicket, name1);

//     std::string name2 = GetThreadName();
//     Thread<std::string> t2(name2, GetTicket, name2);

//     std::string name3 = GetThreadName();
//     Thread<std::string> t3(name3, GetTicket, name3);

//     std::string name4 = GetThreadName();
//     Thread<std::string> t4(name4, GetTicket, name4);

//     t1.Start();
//     t2.Start();
//     t3.Start();
//     t4.Start();

//     t1.Join();
//     t2.Join();
//     t3.Join();
//     t4.Join();

//     return 0;
// }



//局部锁，需要传递给每个线程
int ticket = 10000; 

void GetTicket(pthread_mutex_t *mutex)
{
    while (true)
    {
        pthread_mutex_lock(mutex);
        if (ticket > 0)
        {
            usleep(1000);
            printf("get a ticket: %d\n", ticket);
            ticket--;
            pthread_mutex_unlock(mutex);
        }
        else
        {
            pthread_mutex_unlock(mutex);
            break;
        }
    }
}
int main()
{
    pthread_mutex_t mutex;//注意这时局部的锁，定义方式和全局的略微不同
    pthread_mutex_init(&mutex, nullptr);

    //创建四个线程抢票
    std::string name1 = GetThreadName();
    Thread<pthread_mutex_t *> t1(name1, GetTicket, &mutex);//注意参数，想让线程看到同一把锁

    std::string name2 = GetThreadName();
    Thread<pthread_mutex_t *> t2(name2, GetTicket, &mutex);

    std::string name3 = GetThreadName();
    Thread<pthread_mutex_t *> t3(name3, GetTicket, &mutex);

    std::string name4 = GetThreadName();
    Thread<pthread_mutex_t *> t4(name4, GetTicket, &mutex);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();

    pthread_mutex_destroy(&mutex);
    return 0;
}




